{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 177,
   "metadata": {},
   "outputs": [],
   "source": [
    "from rply import LexerGenerator\n",
    "\n",
    "\n",
    "class Lexer():\n",
    "    def __init__(self):\n",
    "        self.lexer = LexerGenerator()\n",
    "\n",
    "    def _add_tokens(self):\n",
    "        # Print\n",
    "        self.lexer.add('PRINT', r'print')\n",
    "        \n",
    "        self.lexer.add('SQRT', r'sqrt')\n",
    "        \n",
    "        \n",
    "        # Parenthesis\n",
    "        self.lexer.add('OPEN_PAREN', r'\\(')\n",
    "        self.lexer.add('CLOSE_PAREN', r'\\)')\n",
    "        \n",
    "        # Semi Colon\n",
    "        self.lexer.add('SEMI_COLON', r'\\;')\n",
    "        \n",
    "        # Operators\n",
    "        self.lexer.add('SUM', r'\\+')\n",
    "        self.lexer.add('SUB', r'\\-')\n",
    "        self.lexer.add('MUL', r'\\*')\n",
    "        self.lexer.add('DIV', r'\\/')\n",
    "        \n",
    "        # Number\n",
    "        self.lexer.add('NUMBER', r'\\d+')\n",
    "        \n",
    "        # Ignore spaces\n",
    "        self.lexer.ignore('\\s+')\n",
    "\n",
    "    def get_lexer(self):\n",
    "        self._add_tokens()\n",
    "        return self.lexer.build()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 178,
   "metadata": {},
   "outputs": [],
   "source": [
    "# !pip install rply\n",
    "# !pip install ast"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## AST"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 179,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Number():\n",
    "    def __init__(self, value):\n",
    "        self.value = value\n",
    "\n",
    "    def eval(self):\n",
    "        return int(self.value)\n",
    "\n",
    "\n",
    "class BinaryOp():\n",
    "    def __init__(self, left, right):\n",
    "        self.left = left\n",
    "        self.right = right\n",
    "\n",
    "\n",
    "class Sum(BinaryOp):\n",
    "    def eval(self):\n",
    "        return self.left.eval() + self.right.eval()\n",
    "\n",
    "\n",
    "class Sub(BinaryOp):\n",
    "    def eval(self):\n",
    "        return self.left.eval() - self.right.eval()\n",
    "\n",
    "class Mul(BinaryOp):\n",
    "    def eval(self):\n",
    "        return self.left.eval() * self.right.eval()\n",
    "\n",
    "class Div(BinaryOp):\n",
    "    def eval(self):\n",
    "        return self.left.eval() / self.right.eval()\n",
    "\n",
    "\n",
    "class Print():\n",
    "    def __init__(self, value):\n",
    "        self.value = value\n",
    "\n",
    "    def eval(self):\n",
    "        print(self.value.eval())\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 205,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 205,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import fastnumbers\n",
    "fastnumbers.isreal(\"d\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Parser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 199,
   "metadata": {},
   "outputs": [],
   "source": [
    "from rply import ParserGenerator\n",
    "# from ast import Number, Sum, Sub, Print\n",
    "\n",
    "\n",
    "class Parser():\n",
    "    def __init__(self):\n",
    "        self.pg = ParserGenerator(\n",
    "            # A list of all token names accepted by the parser.\n",
    "            ['NUMBER', 'SQRT','PRINT', 'OPEN_PAREN', 'CLOSE_PAREN',\n",
    "             'SEMI_COLON', 'SUM', 'SUB', 'MUL', 'DIV'], precedence=[\n",
    "             ('left',['SUM','SUB']),\n",
    "             ('left',['DIV','MUL'])\n",
    "             ]\n",
    "        )\n",
    "\n",
    "    def parse(self):\n",
    "#         @self.pg.production('function : SQRT OPEN_PAREN expression CLOSE_PAREN')\n",
    "#         def func(p):\n",
    "# #             print(p)\n",
    "#             return (p[2])\n",
    "\n",
    "        \n",
    "        @self.pg.production('program : PRINT OPEN_PAREN expression CLOSE_PAREN SEMI_COLON')\n",
    "        def program(p):\n",
    "            return Print(p[2])\n",
    "\n",
    "        @self.pg.production('expression : expression SUM expression')\n",
    "        @self.pg.production('expression : expression SUB expression')\n",
    "        @self.pg.production('expression : expression MUL expression')\n",
    "        @self.pg.production('expression : expression DIV expression')\n",
    "        def expression(p):\n",
    "            left = p[0]\n",
    "            right = p[2]\n",
    "            operator = p[1]\n",
    "            \n",
    "            if operator.gettokentype() == 'SUM':\n",
    "                return Sum(left, right)\n",
    "            elif operator.gettokentype() == 'SUB':\n",
    "                return Sub(left, right)\n",
    "            elif operator.gettokentype() == 'MUL':\n",
    "                return Mul(left, right)\n",
    "            elif operator.gettokentype() == 'DIV':\n",
    "                return Div(left, right)\n",
    "            \n",
    "\n",
    "        @self.pg.production('expression : NUMBER')\n",
    "        def number(p):\n",
    "            return Number(p[0].value)\n",
    "\n",
    "        @self.pg.error\n",
    "        def error_handle(token):\n",
    "            raise ValueError(token)\n",
    "\n",
    "    def get_parser(self):\n",
    "        return self.pg.build()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 200,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\argenisleon\\Anaconda3\\lib\\site-packages\\ipykernel_launcher.py:55: ParserGeneratorWarning: Token 'SQRT' is unused\n"
     ]
    },
    {
     "ename": "LexingError",
     "evalue": "(None, SourcePosition(idx=7, lineno=-1, colno=-1))",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mLexingError\u001b[0m                               Traceback (most recent call last)",
      "\u001b[1;32m<timed exec>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n",
      "\u001b[1;32m~\\Anaconda3\\lib\\site-packages\\rply\\parser.py\u001b[0m in \u001b[0;36mparse\u001b[1;34m(self, tokenizer, state)\u001b[0m\n\u001b[0;32m     30\u001b[0m                 \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     31\u001b[0m                     \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 32\u001b[1;33m                         \u001b[0mlookahead\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtokenizer\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     33\u001b[0m                     \u001b[1;32mexcept\u001b[0m \u001b[0mStopIteration\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     34\u001b[0m                         \u001b[0mlookahead\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m~\\Anaconda3\\lib\\site-packages\\rply\\lexer.py\u001b[0m in \u001b[0;36m__next__\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m     58\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     59\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__next__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 60\u001b[1;33m         \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m~\\Anaconda3\\lib\\site-packages\\rply\\lexer.py\u001b[0m in \u001b[0;36mnext\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m     55\u001b[0m                 \u001b[1;32mreturn\u001b[0m \u001b[0mtoken\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     56\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 57\u001b[1;33m             \u001b[1;32mraise\u001b[0m \u001b[0mLexingError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mSourcePosition\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0midx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     58\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     59\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__next__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mLexingError\u001b[0m: (None, SourcePosition(idx=7, lineno=-1, colno=-1))"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "text_input = \"\"\"\n",
    "print(SQRT(4*3/2));\n",
    "\"\"\"\n",
    "\n",
    "lexer = Lexer().get_lexer()\n",
    "tokens = lexer.lex(text_input)\n",
    "\n",
    "pg = Parser()\n",
    "pg.parse()\n",
    "parser = pg.get_parser()\n",
    "# print(parser)\n",
    "parser.parse(tokens).eval()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
